package cn.getech.data.development.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.xxl.job.core.log.XxlJobLogger;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Slf4j
public class DruidUtil {
    /**
     * 最小睡眠时间
     */
    private static Long MIN_SLEEP = 5L;
    /**
     * 最大睡眠时间
     */
    private static Long MAX_SLEEP = 60L;
    /**
     * 如果监听线程没事情做， 每次睡眠增加的时间
     */
    private static Long STEP_SLEEP = 10L;

    private static Long CURR_SLEEP = MIN_SLEEP;

    private static String HIVE_TYPE = "hive";
    private static String MYSQL_TYPE = "mysql";
    private static String ORACLE_TYPE = "oracle";

    private static DruidUtil single = null;

    /**
     * 数据连接池map
     */
    private static ConcurrentMap<String, SourceTime> dataSourceMap = new ConcurrentHashMap<>();

    private DruidUtil() {
        listen();
    }

    public void init(String type, String url, String userName, String password){
        Properties prop = new Properties();
        if(MYSQL_TYPE.equalsIgnoreCase(type)){
            prop.setProperty("driver","com.mysql.jdbc.Driver");
            prop.setProperty("url", url);
            prop.setProperty("connectionProperties","useUnicode=true;characterEncoding=UTF8");
        }else if (ORACLE_TYPE.equalsIgnoreCase(type)){
            prop.setProperty("driver","oracle.jdbc.driver.OracleDriver");
            prop.setProperty("url","jdbc:oracle:thin:@"+url);
        }else if(HIVE_TYPE.equalsIgnoreCase(type)){
            prop.setProperty("driver","org.apache.hive.jdbc.HiveDriver");
            prop.setProperty("url", url);
        }else {
            throw new RuntimeException("连接池目前只支持Mysql、Oracle、Hive三种数据库类型！");
        }
        prop.setProperty("username",userName);
        prop.setProperty("password", password);
        prop.setProperty("initialSize","3");
        prop.setProperty("maxActive","50");
        prop.setProperty("minIdle","1");
        prop.setProperty("maxWait","60000");
        prop.setProperty("filters","stat");
        prop.setProperty("timeBetweenEvictionRunsMillis","35000");
        prop.setProperty("minEvictableIdleTimeMillis","30000");
        prop.setProperty("testWhileIdle","true");
        prop.setProperty("testOnBorrow","false");
        prop.setProperty("testOnReturn","false");
        prop.setProperty("poolPreparedStatements","false");
        prop.setProperty("keepAlive","true");
        prop.setProperty("removeAbandoned","true");
        prop.setProperty("removeAbandonedTimeout","1800");
        prop.setProperty("logAbandoned","true");
        try {
            DruidDataSource druidDataSource = (DruidDataSource)DruidDataSourceFactory
                    .createDataSource(prop);
            //druidDataSource.setTimeBetweenConnectErrorMillis(6000);
            druidDataSource.setBreakAfterAcquireFailure(true);
            druidDataSource.setConnectionErrorRetryAttempts(0);
            dataSourceMap.put(type + url + userName, new SourceTime(druidDataSource, new Date().getTime()));
        } catch (Exception e) {
            log.error("初始化创建连接池失败:", e);
            XxlJobLogger.log("xxllo初始化创建连接池失败{}",e.getMessage());
            XxlJobLogger.log("xxllo初始化创建连接池失败{}",e);
        }
    }

    /**
     * 获取实例
     * @return
     */
    public static DruidUtil getInstance(){
        if (single == null) {
            synchronized (DruidUtil.class) {
                if (single == null) {
                    single = new DruidUtil();
                }
            }
        }
        return single;
    }

    private Connection getConnection(String serverUrl, String userName, String password, String type) throws SQLException {
        String key = type + serverUrl + userName;
        log.info("当前已存在的的连接个数:{}",dataSourceMap.keySet().size());
        Connection conn = null;
        synchronized (key){
            if (!dataSourceMap.containsKey(key) || dataSourceMap.get(key).source.isClosed()) {
                init(type, serverUrl, userName, password);
            }
            conn = dataSourceMap.get(key).source.getConnection(6000);
            log.info("key:{}", key);
            dataSourceMap.put(key, new SourceTime(dataSourceMap.get(key).source, new Date().getTime()));
        }

        return conn;
    }

    public Connection getHiveConnection(String serverUrl, String userName, String password) throws SQLException {
        return getConnection(serverUrl, userName, password, HIVE_TYPE);
    }

    public Connection getMysqlConnection(String serverUrl, String userName, String password) throws SQLException {
        return getConnection(serverUrl, userName, password, MYSQL_TYPE);
    }

    public Connection getOracleConnection(String serverUrl, String userName, String password) throws SQLException {
        return getConnection(serverUrl, userName, password, ORACLE_TYPE);
    }

    /**
     * 监听线程 关闭数据连接和关闭数据连接池
     */
    private void listen() {
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(CURR_SLEEP * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (CollectionUtil.isEmpty(dataSourceMap)) {
                    CURR_SLEEP = CURR_SLEEP + STEP_SLEEP;
                    if(CURR_SLEEP>MAX_SLEEP)
                        CURR_SLEEP = MAX_SLEEP;
                    continue;
                } else {
                    CURR_SLEEP = MIN_SLEEP;
                }

                long currTime = new Date().getTime();
                List<String> removeSources = new ArrayList<>();
                for(Map.Entry<String, SourceTime> entry : dataSourceMap.entrySet()){
                    if(currTime - entry.getValue().time > MAX_SLEEP * 8 * 1000){//8分钟
                        try{
                            synchronized (entry.getKey()){
                                entry.getValue().source.close();
                            }
                        }catch (Exception e){
                            e.printStackTrace();
                        }

                        removeSources.add(entry.getKey());
                    }
                }

                if (removeSources.size() > 0) {//删除连接池
                    for (String key : removeSources) {
                        dataSourceMap.remove(key);
                    }
                }
            }
        }).start();
    }

    private class SourceTime{
        public DruidDataSource source;
        public Long time;

        public SourceTime(DruidDataSource source, Long time){
            this.source = source;
            this.time = time;
        }
    }

//    public static void main(String[] args) {
//        try {
//            DruidUtil druidUtil = DruidUtil.getInstance();
//            Connection con = druidUtil.getMysqlConnection("jdbc:mysql://bigdata-test-7:3306/", "root", "1212");
//            System.out.println("获取成功");
//        } catch (Exception e) {
//            log.error(e.getMessage());
//            System.out.println("失败");
//        }
//    }
}
